package com.alex.tkmybatis.common.support;

import com.alex.tkmybatis.common.BusinessException;
import org.apache.commons.lang3.ArrayUtils;
import tk.mybatis.mapper.entity.EntityTable;

import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;

import static java.util.Locale.ENGLISH;

/**
 * @author alexchan
 * lambda表达式解析类
 */
public class LambdaUtils {
    /**
     * 字段映射
     */
    private static final Map<String, Map<String, String>> LAMBDA_MAP = new ConcurrentHashMap<>();

    /**
     * SerializedLambda 反序列化缓存
     */
    private static final Map<Class<?>, WeakReference<SerializedLambda>> FUNC_CACHE = new ConcurrentHashMap<>();


    private LambdaUtils() {
    }

    /**
     * 解析 lambda 表达式, 该方法只是调用了 {@link SerializedLambda#resolve(SFunction)} 中的方法，在此基础上加了缓存。
     * 该缓存可能会在任意不定的时间被清除
     *
     * @param func 需要解析的 lambda 对象
     * @param <T>  类型，被调用的 Function 对象的目标类型
     * @return 返回解析后的结果
     * @see SerializedLambda#resolve(SFunction)
     */
    public static <T> SerializedLambda resolve(SFunction<T, ?> func) {
        Class<?> clazz = func.getClass();
        return Optional.ofNullable(FUNC_CACHE.get(clazz))
                .map(WeakReference::get)
                .orElseGet(() -> {
                    SerializedLambda lambda = SerializedLambda.resolve(func);
                    FUNC_CACHE.put(clazz, new WeakReference<>(lambda));
                    return lambda;
                });
    }

    /**
     * 格式化 key 将传入的 key 变更为大写格式
     *
     * <pre>
     *     Assert.assertEquals("USERID", formatKey("userId"))
     * </pre>
     *
     * @param key key
     * @return 大写的 key
     */
    public static String formatKey(String key) {
        return key.toUpperCase(ENGLISH);
    }

    /**
     * 将传入的表属性信息加入缓存
     *
     * @param tableInfo 表信息
     */
    public static void installCache(EntityTable tableInfo) {
        LAMBDA_MAP.put(tableInfo.getEntityClass().getName(), createColumnCacheMap(tableInfo));
    }

    /**
     * 缓存实体属性字段 MAP 信息
     *
     * @param info 表信息
     * @return 缓存 map
     */
    private static Map<String, String> createColumnCacheMap(EntityTable info) {
        Map<String, String> map = new HashMap<>();
        info.getEntityClassPKColumns().forEach(k ->
                map.put(formatKey(k.getProperty()), k.getProperty())

        );

        info.getEntityClassColumns().forEach(i ->
                map.put(formatKey(i.getProperty()), i.getProperty())
        );
        return map;
    }

    /**
     * 获取实体对应字段 MAP
     *
     * @param clazz 实体类
     * @return 缓存 map
     */
    public static Map<String, String> getColumnMap(Class<?> clazz) {
        return LAMBDA_MAP.getOrDefault(clazz.getName(), Collections.emptyMap());
    }

    /**
     * 安全的进行字符串 format
     *
     * @param target 目标字符串
     * @param params format 参数
     * @return format 后的
     */
    public static String format(String target, Object... params) {
        if (target.contains("%s") && ArrayUtils.isNotEmpty(params)) {
            return String.format(target, params);
        }
        return target;
    }

    /**
     * <p>
     * 请仅在确定类存在的情况下调用该方法
     * </p>
     *
     * @param name 类名称
     * @return 返回转换后的 Class
     */
    public static Class<?> toClassConfident(String name) {
        try {
            return Class.forName(name);
        } catch (ClassNotFoundException e) {
            throw new BusinessException("找不到指定的class！请仅在明确确定会有 class 的时候，调用该方法" + e.getMessage());
        }
    }
}
